"""
测试框架配置和基础工具
提供测试金字塔所需的配置、工具类和基础功能
"""
import os
import sys
import json
import asyncio
import tempfile
from typing import Any, Dict, List, Optional, Union, Callable
from pathlib import Path
from unittest.mock import Mock, MagicMock, patch
import pytest
import pytest_asyncio
from faker import Faker
from datetime import datetime, timedelta
import logging

from utils.config import Config
from utils.logger import Logger
from utils.exceptions import TestException, ErrorCode


class TestConfig:
    """测试配置"""
    
    def __init__(self):
        # 基础配置
        self.test_data_dir = Path("tests/data")
        self.test_logs_dir = Path("tests/logs")
        self.test_temp_dir = Path("tests/temp")
        
        # 创建测试目录
        self.test_data_dir.mkdir(parents=True, exist_ok=True)
        self.test_logs_dir.mkdir(parents=True, exist_ok=True)
        self.test_temp_dir.mkdir(parents=True, exist_ok=True)
        
        # 测试环境配置
        self.environment = "testing"
        self.debug = True
        self.mock_external_services = True
        
        # 数据库配置
        self.db_type = "sqlite"
        self.db_name = "test_maess_flow.db"
        self.db_path = self.test_temp_dir / self.db_name
        
        # Redis配置
        self.redis_host = "localhost"
        self.redis_port = 6380  # 测试端口
        self.redis_db = 15  # 测试数据库
        
        # API配置
        self.api_base_url = "http://localhost:8000"
        self.api_timeout = 30
        
        # 性能测试配置
        self.performance_test_users = 10
        self.performance_test_duration = 60  # 秒
        
        # 覆盖率配置
        self.coverage_enabled = True
        self.coverage_report_dir = Path("tests/coverage")
        self.coverage_threshold = 80  # 最低覆盖率要求


class TestDataGenerator:
    """测试数据生成器"""
    
    def __init__(self):
        self.faker = Faker('zh_CN')  # 使用中文数据
        self.faker_en = Faker()  # 英文数据
    
    def generate_document(self, doc_type: str = "pdf") -> Dict[str, Any]:
        """生成文档测试数据"""
        return {
            "id": self.faker.uuid4(),
            "title": self.faker.sentence(nb_words=6),
            "content": self.faker.text(max_nb_chars=1000),
            "file_path": f"/test/documents/{self.faker.uuid4()}.{doc_type}",
            "file_size": self.faker.random_int(min=1024, max=1024*1024),
            "file_type": doc_type,
            "created_at": self.faker.date_time_between(start_date="-1y", end_date="now"),
            "updated_at": self.faker.date_time_between(start_date="-1m", end_date="now"),
            "author": self.faker.name(),
            "tags": [self.faker.word() for _ in range(self.faker.random_int(min=1, max=5))],
            "metadata": {
                "pages": self.faker.random_int(min=1, max=100),
                "language": "zh-CN",
                "encoding": "utf-8"
            }
        }
    
    def generate_segment(self, segment_type: str = "text") -> Dict[str, Any]:
        """生成分片测试数据"""
        return {
            "id": self.faker.uuid4(),
            "document_id": self.faker.uuid4(),
            "content": self.faker.text(max_nb_chars=500),
            "segment_type": segment_type,
            "position": self.faker.random_int(min=1, max=100),
            "chapter": f"第{self.faker.random_int(min=1, max=10)}章",
            "section": f"第{self.faker.random_int(min=1, max=10)}节",
            "created_at": self.faker.date_time_between(start_date="-1y", end_date="now"),
            "metadata": {
                "word_count": self.faker.random_int(min=10, max=500),
                "language": "zh-CN",
                "confidence": self.faker.random_int(min=80, max=100)
            }
        }
    
    def generate_user(self) -> Dict[str, Any]:
        """生成用户测试数据"""
        return {
            "id": self.faker.uuid4(),
            "username": self.faker.user_name(),
            "email": self.faker.email(),
            "phone": self.faker.phone_number(),
            "full_name": self.faker.name(),
            "created_at": self.faker.date_time_between(start_date="-1y", end_date="now"),
            "last_login": self.faker.date_time_between(start_date="-30d", end_date="now"),
            "is_active": self.faker.boolean(chance_of_getting_true=90),
            "role": self.faker.random_element(["admin", "user", "guest"])
        }
    
    def generate_api_request(self, method: str = "GET") -> Dict[str, Any]:
        """生成API请求测试数据"""
        return {
            "method": method,
            "path": f"/api/{self.faker.random_element(['documents', 'segments', 'search', 'users'])}/{self.faker.uuid4()}",
            "headers": {
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self.faker.uuid4()}",
                "User-Agent": self.faker_en.user_agent()
            },
            "body": json.dumps({
                "query": self.faker.sentence(),
                "filters": {
                    "date_range": {
                        "start": self.faker.date(),
                        "end": self.faker.date()
                    }
                }
            }),
            "query_params": {
                "page": self.faker.random_int(min=1, max=10),
                "size": self.faker.random_int(min=10, max=100)
            }
        }
    
    def generate_config(self) -> Dict[str, Any]:
        """生成配置测试数据"""
        return {
            "app_name": "MAESS-Flow",
            "version": f"{self.faker.random_int(min=1, max=3)}.{self.faker.random_int(min=0, max=9)}.{self.faker.random_int(min=0, max=9)}",
            "environment": self.faker.random_element(["development", "testing", "staging", "production"]),
            "debug": self.faker.boolean(),
            "database": {
                "type": "sqlite",
                "host": "localhost",
                "port": 3306,
                "name": f"test_db_{self.faker.random_int(min=1, max=100)}"
            },
            "redis": {
                "host": "localhost",
                "port": 6379,
                "db": self.faker.random_int(min=0, max=15)
            },
            "api": {
                "host": "0.0.0.0",
                "port": self.faker.random_int(min=8000, max=9000),
                "debug": self.faker.boolean()
            }
        }


class MockFactory:
    """Mock工厂"""
    
    @staticmethod
    def create_document_manager_mock():
        """创建文档管理器Mock"""
        mock = Mock()
        mock.process_document = Mock(return_value={"status": "success", "document_id": "test_doc_123"})
        mock.get_document = Mock(return_value={"id": "test_doc_123", "title": "测试文档"})
        mock.search_documents = Mock(return_value=[{"id": "test_doc_123", "title": "测试文档"}])
        mock.delete_document = Mock(return_value=True)
        return mock
    
    @staticmethod
    def create_segment_analyzer_mock():
        """创建分片分析器Mock"""
        mock = Mock()
        mock.analyze_segments = Mock(return_value=[{"id": "seg_123", "content": "测试内容"}])
        mock.extract_chapters = Mock(return_value=[{"title": "第一章", "start": 0, "end": 100}])
        mock.extract_metadata = Mock(return_value={"title": "测试文档", "author": "测试作者"})
        return mock
    
    @staticmethod
    def create_cache_mock():
        """创建缓存Mock"""
        mock = Mock()
        mock.get = Mock(return_value=None)
        mock.set = Mock(return_value=True)
        mock.delete = Mock(return_value=True)
        mock.exists = Mock(return_value=False)
        mock.clear = Mock(return_value=True)
        return mock
    
    @staticmethod
    def create_redis_mock():
        """创建Redis Mock"""
        mock = Mock()
        mock.get = Mock(return_value=None)
        mock.set = Mock(return_value=True)
        mock.delete = Mock(return_value=True)
        mock.exists = Mock(return_value=False)
        mock.keys = Mock(return_value=[])
        mock.flushdb = Mock(return_value=True)
        return mock
    
    @staticmethod
    def create_database_mock():
        """创建数据库Mock"""
        mock = Mock()
        mock.execute = Mock(return_value=[])
        mock.fetch_one = Mock(return_value=None)
        mock.fetch_all = Mock(return_value=[])
        mock.insert = Mock(return_value=1)
        mock.update = Mock(return_value=1)
        mock.delete = Mock(return_value=1)
        return mock


class TestBase:
    """测试基类"""
    
    def __init__(self):
        self.config = TestConfig()
        self.data_generator = TestDataGenerator()
        self.mock_factory = MockFactory()
        self.logger = Logger.get_logger(agent_name="test_base")
    
    def setup_method(self):
        """测试方法前置设置"""
        self.logger.info("开始测试方法设置")
        self._setup_test_environment()
        self._setup_mocks()
    
    def teardown_method(self):
        """测试方法后置清理"""
        self.logger.info("开始测试方法清理")
        self._cleanup_test_environment()
        self._cleanup_mocks()
    
    def _setup_test_environment(self):
        """设置测试环境"""
        # 设置环境变量
        os.environ['MAESS_ENV'] = 'testing'
        os.environ['MAESS_DEBUG'] = 'true'
        
        # 创建临时目录
        self.temp_dir = tempfile.mkdtemp(dir=self.config.test_temp_dir)
        
        # 初始化配置
        self.test_config = Config()
        self.test_config.set("environment", "testing")
        self.test_config.set("debug", True)
        self.test_config.set("database.type", "sqlite")
        self.test_config.set("database.path", str(self.config.test_temp_dir / "test.db"))
        self.test_config.set("redis.host", self.config.redis_host)
        self.test_config.set("redis.port", self.config.redis_port)
        self.test_config.set("redis.db", self.config.redis_db)
    
    def _cleanup_test_environment(self):
        """清理测试环境"""
        # 清理临时文件
        if hasattr(self, 'temp_dir') and os.path.exists(self.temp_dir):
            import shutil
            shutil.rmtree(self.temp_dir)
        
        # 清理环境变量
        if 'MAESS_ENV' in os.environ:
            del os.environ['MAESS_ENV']
        if 'MAESS_DEBUG' in os.environ:
            del os.environ['MAESS_DEBUG']
    
    def _setup_mocks(self):
        """设置Mock"""
        self.mocks = {}
        
        # 创建常用Mock
        self.mocks['document_manager'] = self.mock_factory.create_document_manager_mock()
        self.mocks['segment_analyzer'] = self.mock_factory.create_segment_analyzer_mock()
        self.mocks['cache'] = self.mock_factory.create_cache_mock()
        self.mocks['redis'] = self.mock_factory.create_redis_mock()
        self.mocks['database'] = self.mock_factory.create_database_mock()
    
    def _cleanup_mocks(self):
        """清理Mock"""
        # 重置所有Mock
        for mock in self.mocks.values():
            if hasattr(mock, 'reset_mock'):
                mock.reset_mock()
    
    def assert_document_structure(self, document: Dict[str, Any]):
        """断言文档结构"""
        required_fields = ['id', 'title', 'content', 'file_path', 'created_at']
        for field in required_fields:
            assert field in document, f"文档缺少必要字段: {field}"
        
        assert isinstance(document['id'], str)
        assert isinstance(document['title'], str)
        assert isinstance(document['content'], str)
        assert isinstance(document['file_path'], str)
    
    def assert_segment_structure(self, segment: Dict[str, Any]):
        """断言分片结构"""
        required_fields = ['id', 'document_id', 'content', 'segment_type', 'position']
        for field in required_fields:
            assert field in segment, f"分片缺少必要字段: {field}"
        
        assert isinstance(segment['id'], str)
        assert isinstance(segment['document_id'], str)
        assert isinstance(segment['content'], str)
        assert isinstance(segment['segment_type'], str)
        assert isinstance(segment['position'], int)
    
    def assert_api_response(self, response: Dict[str, Any], expected_status: str = "success"):
        """断言API响应"""
        assert 'status' in response, "响应缺少status字段"
        assert response['status'] == expected_status, f"期望状态: {expected_status}, 实际状态: {response['status']}"
        
        if expected_status == "success":
            assert 'data' in response, "成功响应缺少data字段"
        else:
            assert 'error' in response, "错误响应缺少error字段"


class AsyncTestBase(TestBase):
    """异步测试基类"""
    
    def __init__(self):
        super().__init__()
        self.loop = None
    
    def setup_method(self):
        """异步测试前置设置"""
        super().setup_method()
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)
    
    def teardown_method(self):
        """异步测试后置清理"""
        if self.loop:
            self.loop.close()
        super().teardown_method()
    
    async def async_setup_method(self):
        """异步测试方法前置设置"""
        pass
    
    async def async_teardown_method(self):
        """异步测试方法后置清理"""
        pass
    
    def run_async(self, coro):
        """运行异步函数"""
        return self.loop.run_until_complete(coro)


# 测试辅助函数
def create_test_client():
    """创建测试客户端"""
    # 这里可以创建FastAPI/Flask测试客户端
    pass


def create_test_database():
    """创建测试数据库"""
    # 创建SQLite测试数据库
    import sqlite3
    
    test_db_path = TestConfig().test_temp_dir / "test.db"
    conn = sqlite3.connect(str(test_db_path))
    
    # 创建测试表
    conn.execute("""
        CREATE TABLE IF NOT EXISTS documents (
            id TEXT PRIMARY KEY,
            title TEXT NOT NULL,
            content TEXT,
            file_path TEXT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    """)
    
    conn.execute("""
        CREATE TABLE IF NOT EXISTS segments (
            id TEXT PRIMARY KEY,
            document_id TEXT NOT NULL,
            content TEXT NOT NULL,
            segment_type TEXT,
            position INTEGER,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (document_id) REFERENCES documents (id)
        )
    """)
    
    conn.commit()
    conn.close()
    
    return str(test_db_path)


def cleanup_test_database(db_path: str):
    """清理测试数据库"""
    try:
        if os.path.exists(db_path):
            os.remove(db_path)
    except Exception as e:
        print(f"清理测试数据库失败: {e}")


# Pytest配置
def pytest_configure(config):
    """pytest配置"""
    # 添加自定义标记
    config.addinivalue_line("markers", "unit: 单元测试")
    config.addinivalue_line("markers", "integration: 集成测试")
    config.addinivalue_line("markers", "e2e: 端到端测试")
    config.addinivalue_line("markers", "performance: 性能测试")
    config.addinivalue_line("markers", "slow: 慢速测试")
    config.addinivalue_line("markers", "external: 依赖外部服务的测试")


def pytest_collection_modifyitems(config, items):
    """修改测试项目"""
    # 添加测试分类
    for item in items:
        if "unit" in item.nodeid:
            item.add_marker(pytest.mark.unit)
        elif "integration" in item.nodeid:
            item.add_marker(pytest.mark.integration)
        elif "e2e" in item.nodeid:
            item.add_marker(pytest.mark.e2e)


# 测试fixture
@pytest.fixture
def test_config():
    """测试配置fixture"""
    return TestConfig()


@pytest.fixture
def test_data_generator():
    """测试数据生成器fixture"""
    return TestDataGenerator()


@pytest.fixture
def mock_factory():
    """Mock工厂fixture"""
    return MockFactory()


@pytest.fixture
def test_base():
    """测试基类fixture"""
    test_base = TestBase()
    test_base.setup_method()
    yield test_base
    test_base.teardown_method()


@pytest.fixture
def async_test_base():
    """异步测试基类fixture"""
    test_base = AsyncTestBase()
    test_base.setup_method()
    yield test_base
    test_base.teardown_method()


@pytest.fixture
def test_database():
    """测试数据库fixture"""
    db_path = create_test_database()
    yield db_path
    cleanup_test_database(db_path)


@pytest.fixture(scope="session")
def event_loop():
    """事件循环fixture"""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest.fixture(scope="session")
def faker():
    """Faker fixture"""
    return Faker()


# 测试工具函数
def assert_response_success(response: Dict[str, Any], message: str = None):
    """断言响应成功"""
    assert response.get('status') == 'success', f"响应失败: {response}"
    if message:
        assert response.get('message') == message, f"消息不匹配: 期望 {message}, 实际 {response.get('message')}"


def assert_response_error(response: Dict[str, Any], error_code: str = None, message: str = None):
    """断言响应错误"""
    assert response.get('status') == 'error', f"期望错误响应: {response}"
    if error_code:
        assert response.get('error', {}).get('code') == error_code, f"错误码不匹配: 期望 {error_code}, 实际 {response.get('error', {}).get('code')}"
    if message:
        assert message in response.get('error', {}).get('message', ''), f"错误消息不匹配: 期望包含 {message}, 实际 {response.get('error', {}).get('message', '')}"


def assert_valid_uuid(uuid_str: str):
    """断言有效的UUID"""
    import uuid
    try:
        uuid.UUID(uuid_str)
    except ValueError:
        assert False, f"无效的UUID: {uuid_str}"


def assert_within_range(value: Union[int, float], min_val: Union[int, float], max_val: Union[int, float], message: str = None):
    """断言值在范围内"""
    assert min_val <= value <= max_val, message or f"值 {value} 不在范围 [{min_val}, {max_val}] 内"


def assert_recent_datetime(dt: datetime, max_seconds: int = 60, message: str = None):
    """断言时间是最近的"""
    now = datetime.now()
    diff = abs((now - dt).total_seconds())
    assert diff <= max_seconds, message or f"时间 {dt} 不在最近 {max_seconds} 秒内"


def retry_async(func: Callable, max_attempts: int = 3, delay: float = 1.0, *args, **kwargs):
    """重试异步函数"""
    async def wrapper():
        for attempt in range(max_attempts):
            try:
                return await func(*args, **kwargs)
            except Exception as e:
                if attempt == max_attempts - 1:
                    raise e
                await asyncio.sleep(delay * (2 ** attempt))  # 指数退避
    
    return wrapper